<!DOCTYPE html>
<html lang="zh-CN">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>学生成绩管理系统</title>
  <script src="https://unpkg.com/vue@3/dist/vue.global.js"></script>
  <style>
    body {
      font-family: Arial, sans-serif;
      max-width: 800px;
      margin: 0 auto;
      padding: 20px;
    }
    .container {
      display: flex;
      flex-direction: column;
      gap: 20px;
    }
    .student-list {
      border: 1px solid #ddd;
      border-radius: 5px;
      overflow: hidden;
    }
    .student-item {
      display: flex;
      padding: 10px;
      border-bottom: 1px solid #eee;
      align-items: center;
    }
    .student-item:last-child {
      border-bottom: none;
    }
    .student-item:hover {
      background-color: #f5f5f5;
    }
    .student-name {
      flex: 1;
      font-weight: bold;
      cursor: pointer;
    }
    .student-score {
      width: 80px;
      text-align: center;
    }
    .student-avg {
      width: 80px;
      text-align: center;
      font-weight: bold;
    }
    .student-actions {
      width: 100px;
      text-align: center;
    }
    .header {
      display: flex;
      background-color: #f0f0f0;
      font-weight: bold;
      padding: 10px;
    }
    .header div {
      cursor: pointer;
    }
    .header div:hover {
      text-decoration: underline;
    }
    .form-container {
      border: 1px solid #ddd;
      border-radius: 5px;
      padding: 20px;
    }
    .form-group {
      margin-bottom: 15px;
    }
    label {
      display: block;
      margin-bottom: 5px;
      font-weight: bold;
    }
    input {
      width: 100%;
      padding: 8px;
      border: 1px solid #ddd;
      border-radius: 4px;
      box-sizing: border-box;
    }
    .error {
      color: red;
      font-size: 12px;
      margin-top: 5px;
    }
    button {
      padding: 8px 15px;
      background-color: #4CAF50;
      color: white;
      border: none;
      border-radius: 4px;
      cursor: pointer;
      margin-right: 10px;
    }
    button:hover {
      background-color: #45a049;
    }
    button.delete {
      background-color: #f44336;
    }
    button.delete:hover {
      background-color: #d32f2f;
    }
    .modal {
      position: fixed;
      top: 0;
      left: 0;
      width: 100%;
      height: 100%;
      background-color: rgba(0, 0, 0, 0.5);
      display: flex;
      justify-content: center;
      align-items: center;
      z-index: 1000;
    }
    .modal-content {
      background-color: white;
      padding: 20px;
      border-radius: 5px;
      width: 400px;
      max-width: 90%;
    }
    .modal-header {
      display: flex;
      justify-content: space-between;
      align-items: center;
      margin-bottom: 20px;
    }
    .modal-title {
      font-size: 1.2em;
      font-weight: bold;
    }
    .close {
      cursor: pointer;
      font-size: 1.5em;
    }
    .virtual-scroll-container {
      height: 400px;
      overflow-y: auto;
      position: relative;
    }
    .virtual-scroll-content {
      position: relative;
    }
    .student-item-virtual {
      position: absolute;
      width: 100%;
      left: 0;
    }
  </style>
</head>
<body>
  <div id="app">
    <div class="container">
      <h1>学生成绩管理系统</h1>

      <div class="form-container">
        <h2>添加学生</h2>
        <div class="form-group">
          <label for="name">姓名</label>
          <input type="text" id="name" v-model="newStudent.name" @blur="validateName">
          <div class="error" v-if="errors.name">{{ errors.name }}</div>
        </div>
        <div class="form-group">
          <label for="math">数学成绩</label>
          <input type="number" id="math" v-model="newStudent.math" @blur="validateMath">
          <div class="error" v-if="errors.math">{{ errors.math }}</div>
        </div>
        <div class="form-group">
          <label for="english">英语成绩</label>
          <input type="number" id="english" v-model="newStudent.english" @blur="validateEnglish">
          <div class="error" v-if="errors.english">{{ errors.english }}</div>
        </div>
        <button @click="addStudent">添加学生</button>
      </div>

      <h2>学生成绩列表</h2>
      <div class="student-list">
        <div class="header">
          <div class="student-name" @click="sortBy('name')">姓名
            <span v-if="sortField === 'name'">{{ sortDirection === 'asc' ? '↑' : '↓' }}</span>
          </div>
          <div class="student-score" @click="sortBy('math')">数学
            <span v-if="sortField === 'math'">{{ sortDirection === 'asc' ? '↑' : '↓' }}</span>
          </div>
          <div class="student-score" @click="sortBy('english')">英语
            <span v-if="sortField === 'english'">{{ sortDirection === 'asc' ? '↑' : '↓' }}</span>
          </div>
          <div class="student-avg" @click="sortBy('avg')">平均分
            <span v-if="sortField === 'avg'">{{ sortDirection === 'asc' ? '↑' : '↓' }}</span>
          </div>
          <div class="student-actions">操作</div>
        </div>

        <div class="virtual-scroll-container" ref="scrollContainer" @scroll="handleScroll">
          <div class="virtual-scroll-content" :style="{ height: totalHeight + 'px' }">
            <div
              class="student-item-virtual"
              v-for="student in visibleStudents"
              :key="student.id"
              :style="{ top: student.top + 'px', height: itemHeight + 'px' }"
            >
              <div class="student-item">
                <div class="student-name" @click="showStudentDetail(student)">{{ student.name }}</div>
                <div class="student-score">{{ student.math }}</div>
                <div class="student-score">{{ student.english }}</div>
                <div class="student-avg">{{ calculateAvg(student).toFixed(1) }}</div>
                <div class="student-actions">
                  <button class="delete" @click="removeStudent(student.id)">删除</button>
                </div>
              </div>
            </div>
          </div>
        </div>
      </div>

      <div class="modal" v-if="showModal">
        <div class="modal-content">
          <div class="modal-header">
            <div class="modal-title">学生详情</div>
            <div class="close" @click="showModal = false">×</div>
          </div>
          <div v-if="selectedStudent">
            <p><strong>姓名:</strong> {{ selectedStudent.name }}</p>
            <p><strong>数学成绩:</strong> {{ selectedStudent.math }}</p>
            <p><strong>英语成绩:</strong> {{ selectedStudent.english }}</p>
            <p><strong>平均分:</strong> {{ calculateAvg(selectedStudent).toFixed(1) }}</p>
          </div>
        </div>
      </div>
    </div>
  </div>

  <script>
    const { ref, computed, onMounted } = Vue;

    const app = Vue.createApp({
      setup() {
        // 学生数据
        const students = ref([
          { id: 1, name: "张三", math: 90, english: 85 },
          { id: 2, name: "李四", math: 78, english: 92 },
          { id: 3, name: "王五", math: 85, english: 88 },
          { id: 4, name: "赵六", math: 92, english: 76 },
          { id: 5, name: "钱七", math: 65, english: 95 },
          { id: 6, name: "孙八", math: 88, english: 82 },
          { id: 7, name: "周九", math: 72, english: 78 },
          { id: 8, name: "吴十", math: 95, english: 90 },
          { id: 9, name: "郑十一", math: 81, english: 84 },
          { id: 10, name: "王十二", math: 76, english: 79 },
          { id: 11, name: "李十三", math: 89, english: 93 },
          { id: 12, name: "张十四", math: 94, english: 87 },
          { id: 13, name: "刘十五", math: 77, english: 85 },
          { id: 14, name: "陈十六", math: 83, english: 91 },
          { id: 15, name: "杨十七", math: 90, english: 88 },
          { id: 16, name: "黄十八", math: 68, english: 74 },
          { id: 17, name: "赵十九", math: 79, english: 82 },
          { id: 18, name: "吴二十", math: 86, english: 89 },
        ]);

        // 新学生表单
        const newStudent = ref({
          name: '',
          math: '',
          english: ''
        });

        // 表单验证错误
        const errors = ref({
          name: '',
          math: '',
          english: ''
        });

        // 排序相关
        const sortField = ref('id');
        const sortDirection = ref('asc');

        // 模态框相关
        const showModal = ref(false);
        const selectedStudent = ref(null);

        // 虚拟滚动相关
        const scrollContainer = ref(null);
        const itemHeight = 50; // 每个学生项的高度
        const visibleCount = ref(10); // 可视区域显示的数量
        const startIndex = ref(0);
        const scrollTop = ref(0);

        // 计算属性
        const sortedStudents = computed(() => {
          return [...students.value].sort((a, b) => {
            let fieldA, fieldB;

            if (sortField.value === 'avg') {
              fieldA = calculateAvg(a);
              fieldB = calculateAvg(b);
            } else {
              fieldA = a[sortField.value];
              fieldB = b[sortField.value];
            }

            if (typeof fieldA === 'string') {
              return sortDirection.value === 'asc'
                ? fieldA.localeCompare(fieldB)
                : fieldB.localeCompare(fieldA);
            } else {
              return sortDirection.value === 'asc'
                ? fieldA - fieldB
                : fieldB - fieldA;
            }
          });
        });

        // 虚拟滚动相关计算属性
        const totalHeight = computed(() => {
          return students.value.length * itemHeight;
        });

        const visibleStudents = computed(() => {
          return sortedStudents.value.slice(startIndex.value, startIndex.value + visibleCount.value).map((student, index) => {
            return {
              ...student,
              top: (startIndex.value + index) * itemHeight
            };
          });
        });

        // 方法
        const calculateAvg = (student) => {
          return (parseInt(student.math) + parseInt(student.english)) / 2;
        };

        const addStudent = () => {
          // 验证表单
          validateName();
          validateMath();
          validateEnglish();

          // 如果有错误，不添加
          if (errors.value.name || errors.value.math || errors.value.english) {
            return;
          }

          // 生成新ID
          const newId = students.value.length > 0
            ? Math.max(...students.value.map(s => s.id)) + 1
            : 1;

          // 添加新学生
          students.value.push({
            id: newId,
            name: newStudent.value.name,
            math: parseInt(newStudent.value.math),
            english: parseInt(newStudent.value.english)
          });

          // 重置表单
          newStudent.value = {
            name: '',
            math: '',
            english: ''
          };
        };

        const removeStudent = (id) => {
          students.value = students.value.filter(student => student.id !== id);
        };

        const sortBy = (field) => {
          if (sortField.value === field) {
            sortDirection.value = sortDirection.value === 'asc' ? 'desc' : 'asc';
          } else {
            sortField.value = field;
            sortDirection.value = 'asc';
          }
        };

        const showStudentDetail = (student) => {
          selectedStudent.value = student;
          showModal.value = true;
        };

        // 表单验证方法
        const validateName = () => {
          if (!newStudent.value.name) {
            errors.value.name = '姓名不能为空';
          } else if (newStudent.value.name.length > 10) {
            errors.value.name = '姓名不能超过10个字符';
          } else {
            errors.value.name = '';
          }
        };

        const validateMath = () => {
          const score = parseInt(newStudent.value.math);
          if (isNaN(score)) {
            errors.value.math = '请输入有效的数学成绩';
          } else if (score < 0 || score > 100) {
            errors.value.math = '成绩必须在0-100之间';
          } else {
            errors.value.math = '';
          }
        };

        const validateEnglish = () => {
          const score = parseInt(newStudent.value.english);
          if (isNaN(score)) {
            errors.value.english = '请输入有效的英语成绩';
          } else if (score < 0 || score > 100) {
            errors.value.english = '成绩必须在0-100之间';
          } else {
            errors.value.english = '';
          }
        };

        // 虚拟滚动处理方法
        const handleScroll = () => {
          if (scrollContainer.value) {
            scrollTop.value = scrollContainer.value.scrollTop;
            startIndex.value = Math.floor(scrollTop.value / itemHeight);

            // 计算可视区域能显示多少个学生项
            const containerHeight = scrollContainer.value.clientHeight;
            visibleCount.value = Math.ceil(containerHeight / itemHeight) + 2; // 缓冲2个

            // 确保startIndex在合理范围内
            startIndex.value = Math.max(0, Math.min(
              startIndex.value,
              students.value.length - visibleCount.value
            ));
          }
        };

        // 生命周期钩子
        onMounted(() => {
          // 初始化虚拟滚动
          if (scrollContainer.value) {
            const containerHeight = scrollContainer.value.clientHeight;
            visibleCount.value = Math.ceil(containerHeight / itemHeight) + 2;
          }
        });

        return {
          students,
          newStudent,
          errors,
          sortField,
          sortDirection,
          showModal,
          selectedStudent,
          scrollContainer,
          visibleStudents,
          totalHeight,
          itemHeight,

          calculateAvg,
          addStudent,
          removeStudent,
          sortBy,
          showStudentDetail,
          validateName,
          validateMath,
          validateEnglish,
          handleScroll
        };
      }
    });

    app.mount('#app');
  </script>
</body>
</html>
